package cn.springcloud.book.config;

import cn.springcloud.book.common.UserUtil;
import cn.springcloud.book.handler.CustomWebResponseExceptionTranslator;
import cn.springcloud.book.jwt.MyJwtAccessTokenConverter;
import cn.springcloud.book.security.UserDetailsImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableAuthorizationServer
@Slf4j
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private CustomWebResponseExceptionTranslator customWebResponseExceptionTranslator;

//    @Autowired
//    private UserDetailsServiceImpl userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        //return new BCryptPasswordEncoder();
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient("zuul_server")
                .secret(passwordEncoder().encode("secret"))
//                .secret("secret")
                .scopes("server").autoApprove(true)
                .authorities("write")
                .authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code");

    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // token增强链
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        // 把jwt增强，与额外信息增强加入到增强链
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtTokenConverter()));

        endpoints
                .tokenServices(tokenServices())
                //tokenServices 的加入可能不需要下面的配置-待测试
                //token的存储位置
                .tokenStore(jwtTokenStore())
//                .tokenEnhancer(jwtTokenConverter())
                //增强模式
                .tokenEnhancer(tokenEnhancerChain)
                //token的生成方式
                .accessTokenConverter(jwtTokenConverter())
//                .userDetailsService(userDetailsService)
                .authenticationManager(authenticationManager);

        // 添加认证异常处理器
        endpoints.exceptionTranslator(customWebResponseExceptionTranslator);
    }


    /**
     * 重写 DefaultTokenServices 设置tokrn 有效时间
     * public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception的配置中添加.tokenServices(mPTokenService())。
     *
     * @return
     */
    @Primary
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setAccessTokenValiditySeconds(3000);
        defaultTokenServices.setRefreshTokenValiditySeconds(72000);
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setReuseRefreshToken(true);
        defaultTokenServices.setTokenStore(jwtTokenStore());
//        defaultTokenServices.setTokenEnhancer(jwtTokenConverter());
        //增强
        // token增强链
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        // 把jwt增强，与额外信息增强加入到增强链
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtTokenConverter()));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
        return defaultTokenServices;
    }

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtTokenConverter()) {
            @Override
            public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
                super.storeAccessToken(token, authentication);
                //存储token jwtTokenStore是空实现
                //此处根据token获取userId存储到redis，来做单点的登录
                String key = Base64.getEncoder().encodeToString("springcloud123".getBytes());
                String value = token.getValue();
                Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(value).getBody();
                Integer userId = (Integer) claims.get("userId");
                log.info("获取userId成功，值为" + userId);
                log.info("存储token");
            }
        };
    }

    @Bean
    protected JwtAccessTokenConverter jwtTokenConverter() {
//        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//        converter.setSigningKey("springcloud123");
        //改造--实现增加携带参数
//        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getPassword().toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyProperties.getKeyStore().getAlias()));
        converter.setAccessTokenConverter(new MyJwtAccessTokenConverter());
        converter.setSigningKey("springcloud123");
        return converter;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 允许表单认证请求
                .allowFormAuthenticationForClients()
                // spel表达式 访问公钥端点（/auth/token_key）需要认证
                .tokenKeyAccess("isAuthenticated()")
                // spel表达式 访问令牌解析端点（/auth/check_token）需要认证
                .checkTokenAccess("isAuthenticated()");
    }


    /**
     * jwt token增强，添加额外信息
     *
     * @return
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new TokenEnhancer() {
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {

                // 添加额外信息的map
                final Map<String, Object> additionMessage = new HashMap<>(2);
                // 获取当前登录的用户
                Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();
                //刷新token的时候丢掉了 userName 和 Id 因为 userAuthentication == null
                if (userAuthentication != null) {
                    //刷新token的时候userAuthentication == null
                    UserDetailsImpl user = (UserDetailsImpl) userAuthentication.getPrincipal();
                    // 如果用户不为空 则把id放入jwt token中
                    if (user != null) {
                        additionMessage.put("userId", user.getUserId());
                        additionMessage.put("userName", user.getUsername());
                    }
                } else {
                    RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
                    HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
//                    Integer userId = UserUtil.getUserId(request);
                    String authorization = request.getParameter("refresh_token");
                    Claims claims = UserUtil.getClaims(authorization);
                    Integer userId = (Integer) claims.get("userId");
                    String userName = (String) claims.get("userName");
                    additionMessage.put("userId", userId);
                    additionMessage.put("userName", userName);
                    log.info("没有通过userAuthentication获取的userId:" + userId);
                }
                ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionMessage);
                return oAuth2AccessToken;
            }
        };
    }

}
